﻿

using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Collections.Generic;


namespace QuantumQuest
{
    public class QQPhysics
    {        

        public const double G = 6.67e-11;       
       
        public QQPhysics()
        {
            
        }

        //the next two functions change NOTHING but the position; take accel to be constant regardless of IsConstantAccel;
        //maybe I should add a third to 
        public QQBody UpdatePosition(QQBody body, double dt)
        {
            if (body.IsStationary)
            {
                body.VelocityVec = new QQVector2(0.0,0.0);
                body.AccelerationVec= new QQVector2(0.0,0.0);
                return body;
            }         

            QQBody tempBody = body;
            tempBody.VelocityVec += tempBody.AccelerationVec * dt;
            tempBody.PositionVec += tempBody.VelocityVec * dt + (0.5) * (tempBody.AccelerationVec) * (Math.Pow(dt, 2));
            return tempBody;

        }
        public QQBody UpdatePosition(QQList bodyList, int index, double dt)
        {
            QQBody tempBody = (QQBody)bodyList[index];
            if (tempBody.IsStationary)
            {
                tempBody.VelocityVec = new QQVector2(0.0, 0.0);
                tempBody.AccelerationVec = new QQVector2(0.0, 0.0);
                return tempBody;
            }            
            tempBody.VelocityVec += tempBody.AccelerationVec * dt;
            tempBody.PositionVec += tempBody.VelocityVec * dt + (0.5) * (tempBody.AccelerationVec) * (Math.Pow(dt, 2));
            return tempBody;
        }
       

        public QQList ApplyGravity(QQList bodyList, double dt)
        {

            //todo
            QQList newList = bodyList;
            QQVector2 dir = new QQVector2(0,0);
            
            for (int i = 0; i < bodyList.Count; i++)
            {
                if (((QQBody)newList[i]).IsConstantAccel)
                {
                    continue;
                }
                if (!((QQBody)newList[i]).IsStationary)
                {
                    //HERE
                }
                else
                {
                    ((QQBody)newList[i]).AccelerationVec = new QQVector2(0.0, 0.0);
                    ((QQBody)newList[i]).VelocityVec = new QQVector2(0.0, 0.0);
                }
            }


            return newList;

        }
        public QQBody ApplyGravity(QQList bodyList, QQBody body, double dt)
        {
            
            int index = bodyList.IndexOf(body);
            return ApplyGravity(bodyList, index, dt);
        }
        public QQBody ApplyGravity2(QQList bodies, int index, double dt)
        {
            if (((QQBody)bodies[index]).IsConstantAccel)
            {
                QQBody ret = new QQBody();
                ret.IsConstantAccel = true;
                ret.VelocityVec = ((QQBody)bodies[index]).VelocityVec + ((QQBody)bodies[index]).AccelerationVec * dt;
                ret.PositionVec = ((QQBody)bodies[index]).PositionVec + ((QQBody)bodies[index]).VelocityVec * dt + (0.5) * (((QQBody)bodies[index]).AccelerationVec) * (Math.Pow(dt, 2));
                return ret;
            }
            if (((QQBody)bodies[index]).IsStationary)
            {
                QQBody ret = new QQBody();
                ret.PositionVec = ((QQBody)bodies[index]).PositionVec;
                ret.IsStationary = true;
                ret.VelocityVec = new QQVector2(0, 0);
                ret.AccelerationVec = new QQVector2(0, 0);
                return ret;

            }

            QQBody rhett = new QQBody();
            rhett.AccelerationVec = new QQVector2(0, 0);

            for (int i = 0; i < bodies.Count; i++)
            {
                if (i == index)
                {
                    continue;
                }
                
                QQVector2 difference = new QQVector2(0,0);                
                double difference_mag = 0.0;

                difference = ((QQBody)bodies[i]).PositionVec - ((QQBody)bodies[index]).PositionVec;
                difference_mag = difference.Magnitude;

                if (difference_mag >= 0.01)
                {
                    difference.Magnitude = (G * (((QQBody)bodies[i]).Mass)) / (Math.Pow(difference_mag, 2));
                    rhett.AccelerationVec += difference;
                }
                //else
                //{

                //}
            }
            double o_vx = ((QQBody)bodies[index]).VelocityVec.X;
            double o_vy = ((QQBody)bodies[index]).VelocityVec.Y;
            double o_px = ((QQBody)bodies[index]).PositionVec.X;
            double o_py = ((QQBody)bodies[index]).PositionVec.Y;

            double n_vx;
            double n_vy;
            double n_px;
            double n_py;
            double n_ax= rhett.AccelerationVec.X;
            double n_ay= rhett.AccelerationVec.Y;

            n_vx = o_vx + n_ax * dt;
            n_vy = o_vy + n_ay * dt;

            n_px = o_px + n_vx * dt + (0.5) * (n_ax) * (dt * dt);
            n_py = o_py + n_vy * dt + (0.5) * (n_ay) * (dt * dt);

            //rhett.VelocityVec = ((QQBody)bodies[index]).VelocityVec + rhett.AccelerationVec * dt;
            //rhett.PositionVec = ((QQBody)bodies[index]).PositionVec + rhett.VelocityVec * dt + (0.5) * (rhett.AccelerationVec) * (Math.Pow(dt, 2));

            QQBody rhett2 = new QQBody(new QQVector2(n_px, n_py), new QQVector2(n_vx, n_vy), new QQVector2(n_ax, n_ay), ((QQBody)bodies[index]).Mass, ((QQBody)bodies[index]).ZIndex, ((QQBody)bodies[index]).IsStationary, ((QQBody)bodies[index]).IsConstantAccel);
            return rhett2;
            //return rhett;
            
        }

        public QQBody ApplyGravity(QQList bodyList, int index, double dt)
        {
            QQBody tempBody = (QQBody)bodyList[index];
            QQVector2 dir = new QQVector2(0, 0);
            QQVector2 dirMag = new QQVector2(0, 0);
            QQVector2 dist = new QQVector2(0, 0);
            tempBody.AccelerationVec = new QQVector2(0, 0);

            if (tempBody.IsConstantAccel)
            {
                tempBody.VelocityVec += tempBody.AccelerationVec * dt;
                tempBody.PositionVec += tempBody.VelocityVec * dt + (0.5) * (tempBody.AccelerationVec) * (Math.Pow(dt, 2));
                return tempBody;
            }
            if (!((QQBody)bodyList[index]).IsStationary)
            {
                for (int i = 0; i < bodyList.Count; i++)
                {
                    if (i != index)
                    {
                        dir = tempBody.PositionVec - ((QQBody)(bodyList[i])).PositionVec;
                        dist = dir;
                        dir.Normalize();
                        dirMag = dir;
                        
                        dirMag.Magnitude = (G * (tempBody.Mass * ((QQBody)(bodyList[i])).Mass)) / (Math.Pow(dist.Magnitude, 2));
                        tempBody.AccelerationVec += dirMag;
                    }
                }
                tempBody.VelocityVec += tempBody.AccelerationVec * dt;
                tempBody.PositionVec += tempBody.VelocityVec * dt + (0.5) * (tempBody.AccelerationVec) * (Math.Pow(dt, 2));
            }
            else
            {
                tempBody.AccelerationVec = new QQVector2(0.0, 0.0);
                tempBody.VelocityVec = new QQVector2(0.0, 0.0);
            }
            return tempBody;
        }
        
        public void UpdateCollisions3(ref QQList list)
        {
            for (int i = 0; i < list.Count; i++)
            {
                for (int j = i+1; j < list.Count; j++)
                {
                    if ((Collide((QQBody)list[i], (QQBody)list[j])))
                    {
                        ((QQBody)list[i]).VelocityVec *= -1.0;
                        ((QQBody)list[j]).VelocityVec *= -1.0;
                        QQVector2 distDiff = new QQVector2(0, 0);


                        distDiff = ((QQBody)list[i]).PositionVec - ((QQBody)list[j]).PositionVec;
                        distDiff.Normalize();
                        distDiff.Magnitude *= 0.5;
                        //distDiff.Magnitude -= (((QQBody)list[i]).CollisionRadius + ((QQBody)list[j]).CollisionRadius)
                        if (((QQBody)list[i]).Mass > ((QQBody)list[j]).Mass)
                        {
                            ((QQBody)list[j]).PositionVec.X -= distDiff.X;
                            ((QQBody)list[j]).PositionVec.Y -= distDiff.Y;
                        }
                        else
                        {
                            ((QQBody)list[i]).PositionVec.X += distDiff.X;
                            ((QQBody)list[i]).PositionVec.Y += distDiff.Y;
                        }
                        
                        /*double vx21 = ((QQBody)list[i]).VelocityVec.X - ((QQBody)list[j]).VelocityVec.X;
                        double vy21 = ((QQBody)list[i]).VelocityVec.Y - ((QQBody)list[j]).VelocityVec.Y;
                        double m21 = (((QQBody)list[i]).Mass) / (((QQBody)list[j]).Mass);
                        double x21 = ((QQBody)list[i]).PositionVec.X - ((QQBody)list[j]).PositionVec.X;
                        double y21 = ((QQBody)list[i]).PositionVec.Y - ((QQBody)list[j]).PositionVec.Y;

                        if (Math.Abs(x21) < (Math.Abs(y21) * 1.0E-12))
                        {
                            if (x21 < 0)
                            {
                                x21 = (Math.Abs(y21) * 1.0E-12) * (-1.0);
                            }
                            else
                            {
                                x21 = (Math.Abs(y21) * 1.0E-12);
                            }
                        }

                        double a = y21 / x21;
                        double dvx2 = -2 * (vx21 + a * vy21) / ((1 + a * a) * (1 + m21));
                        ((QQBody)list[j]).VelocityVec.X = ((QQBody)list[j]).VelocityVec.X + dvx2;
                        ((QQBody)list[j]).VelocityVec.Y = ((QQBody)list[j]).VelocityVec.Y + a * dvx2;
                        ((QQBody)list[i]).VelocityVec.X = ((QQBody)list[i]).VelocityVec.X - m21 * dvx2;
                        ((QQBody)list[i]).VelocityVec.Y = ((QQBody)list[i]).VelocityVec.Y - a * m21 * dvx2;*/

                    }
                }
            }
        }
        public bool Collide(QQBody b1, QQBody b2)
        {
            QQVector2 dist = new QQVector2(0, 0);
            dist = b1.PositionVec - b2.PositionVec;
            if ((b1.CollisionRadius + b2.CollisionRadius) >= dist.Magnitude)
            {
                return true;
            }
            else
            {
                return false;
            }
        }
        /*
           public void UpdateCollisions(ref QQList list)
        {
            for (int i = 0; i < list.Count; i++)
            {
                for (int j = i; j < list.Count; j++)
                {
                    if(AreInContact((QQBody)list[i],(QQBody)list[j]))
                    {
                        ((QQBody)list[i]).VelocityVec.X *= -1.0;
                        ((QQBody)list[i]).VelocityVec.Y *= -1.0;

                        ((QQBody)list[j]).VelocityVec.X *= -1.0;
                        ((QQBody)list[j]).VelocityVec.Y *= -1.0;
                    }
                }
            }

        }
        public void UpdateCollisions2(ref QQList list)
        {
            for (int i = 0; i < list.Count; i++)
            {
                for (int j = i; j < list.Count; j++)
                {
                    if (CheckCollision((QQBody)list[i], ((QQBody)list[i]).OutlinePath,(QQBody)list[j], ((QQBody)list[j]).OutlinePath))
                    {
                        ((QQBody)list[i]).VelocityVec.X *= -1.0;
                        ((QQBody)list[i]).VelocityVec.Y *= -1.0;

                        ((QQBody)list[j]).VelocityVec.X *= -1.0;
                        ((QQBody)list[j]).VelocityVec.Y *= -1.0;
                    }
                }
            }

        }
        //this should do the trick, though it could be slow?  i am thinking the most that the covering rectangle intersection is 100-200 points?  which is not bad
        public bool AreInContact(QQBody s1, QQBody s2)
        {
            Rect r1 = new Rect(Canvas.GetLeft(s1),Canvas.GetTop(s1), s1.Width, s1.Height);
            Rect r2 = new Rect(Canvas.GetLeft(s2), Canvas.GetTop(s2), s2.Width,s2.Height);
            // Bounding box collision
            r1.Intersect(r2);
            if (r1.IsEmpty)
            {
                return false;
            }
            Point pt = new Point();
            int l = (int)r1.Left;
            int r = (int)r1.Right;
            int t = (int)r1.Top;
            int b = (int)r1.Bottom;
            
            // Now perform a pixel
            // perfection collision detection
            // by hit testing every point
            //	within the overlapping Rect
            for (int x = l; x <= r; x++)
            {
                for (int y = t; y <= b; y++)
                {
                    pt.X = x;
                    pt.Y = y;
                    List<UIElement> hits1 = System.Windows.Media.VisualTreeHelper.FindElementsInHostCoordinates(pt, s1) as List<UIElement>;
                    List<UIElement> hits2 = System.Windows.Media.VisualTreeHelper.FindElementsInHostCoordinates(pt, s2) as List<UIElement>;
                    
                    if((hits1.Count!=0)&&(hits2.Count!=0))
                    {
                        return true;
                    }
                }
            }
            return false;
        }


        public bool CircleIntersect(Ellipse c1, Ellipse c2)
        {
            if ((c1.Width != c1.Height) || (c2.Width != c2.Height))
            {
                throw new Exception("Not Circles!");
            }

            double dist = Math.Sqrt( Math.Pow(((double)c1.GetValue(Canvas.TopProperty) - (double)c2.GetValue(Canvas.TopProperty)),2)  + Math.Pow(((double)c1.GetValue(Canvas.LeftProperty) - (double)c2.GetValue(Canvas.LeftProperty)),2));
            if (dist <= (c1.Width + c2.Width))
            {
                return true;
            }
            return false;
        }
        public UIElement GetCover(QQBody testBody)
        {
            if (testBody is QQPlanet)
            {
                Ellipse retEllipse = new Ellipse();
                retEllipse.Width = ((QQPlanet)testBody).Width;
                retEllipse.Height = ((QQPlanet)testBody).Width;
                return retEllipse;
            }

            return null;

        }
        public Rect GetUserControlCover(FrameworkElement control)
        {
            Point ptTopLeft = new Point(Convert.ToDouble(control.GetValue(Canvas.LeftProperty)), Convert.ToDouble(control.GetValue(Canvas.TopProperty)));
            Point ptBottomRight = new Point(Convert.ToDouble(control.GetValue(Canvas.LeftProperty)) + control.Width, Convert.ToDouble(control.GetValue(Canvas.TopProperty)) + control.Height);

            return new Rect(ptTopLeft, ptBottomRight);

        }
        
        private bool CheckCollision(FrameworkElement control1, FrameworkElement controlElem1, FrameworkElement control2, FrameworkElement controlElem2)
        {
            // first see if sprite rectangles collide
            Rect rect1 = GetUserControlCover(control1);
            Rect rect2 = GetUserControlCover(control2);


            rect1.Intersect(rect2);
            if (rect1 == Rect.Empty)
            {
                // no collision - GET OUT!
                return false;
            }
            else
            {
                bool bCollision = false;
                Point ptCheck = new Point();

                // now we do a more accurate pixel hit test
                for (int x = Convert.ToInt32(rect1.X); x < Convert.ToInt32(rect1.X + rect1.Width); x++)
                {
                    for (int y = Convert.ToInt32(rect1.Y); y < Convert.ToInt32(rect1.Y + rect1.Height); y++)
                    {
                        ptCheck.X = x;
                        ptCheck.Y = y;

                        List<UIElement> hits = System.Windows.Media.VisualTreeHelper.FindElementsInHostCoordinates(ptCheck, control1) as List<UIElement>;
                        if (hits.Contains(controlElem1))
                        {
                            // we have a hit on the first control elem, now see if the second elem has a similar hit
                            List<UIElement> hits2 = System.Windows.Media.VisualTreeHelper.FindElementsInHostCoordinates(ptCheck, control2) as List<UIElement>;
                            if (hits2.Contains(controlElem2))
                            {
                                bCollision = true;
                                break;
                            }
                        }
                    }
                    if (bCollision) break;
                }
                return bCollision;
            }


        }*/


    }
}
